/**************************************************************************
 *                                                                        *
 *                                                                        *
 *          This code is copyright (c) 1992                               *
 *                     Athena Design, Inc.                                *
 *                     and David Pollak                                   *
 *                                                                        *
 *                ALL RIGHTS RESERVED                                     *
 *                                                                        *
 *                                                                        *
 *                                                                        *
 *                                                                        *
 *                                                                        *
 **************************************************************************/

#import "MesaAPI.h"
#import <servers/netname.h>
#import <appkit/Listener.h>
#import <libc.h>
#import <streams/streams.h>
#import <appkit/publicWraps.h>
#import <dpsclient/dpsNeXT.h>
#import <appkit/NXImage.h>

@implementation MesaAPI

- initToWorksheet:(const char *)ws
{
	kern_return_t r;
	
	[super init];
	port_allocate(task_self(), &myPort);
	r = netname_look_up(name_server_port,"","Mesa-MAPI-1",&mesaPort);
	if (r != NETNAME_SUCCESS) {
		if (NXPortFromName("Mesa", "")) {
			r = netname_look_up(name_server_port,"","Mesa-MAPI-1",&mesaPort);
			if (r != NETNAME_SUCCESS) {
				NXBeep();
				[self free];
				return NULL;
				}
			}
		else {
			NXBeep();
			[self free];
			return NULL;
			}
		}

	theMessage.head.msg_simple = FALSE;
	theMessage.head.msg_size = sizeof(MAPIMessage);
	theMessage.head.msg_type = MSG_TYPE_NORMAL;
	theMessage.head.msg_local_port = myPort;
	theMessage.head.msg_remote_port = mesaPort;

	theMessage.lenType.msg_type_name = MSG_TYPE_INTEGER_32;
	theMessage.lenType.msg_type_size = 32;
	theMessage.lenType.msg_type_number = 1;
	theMessage.lenType.msg_type_inline = TRUE;
	theMessage.lenType.msg_type_longform = FALSE;
	theMessage.lenType.msg_type_deallocate = FALSE;

	theMessage.handleType = theMessage.lenType;
	
	theMessage.dataType.msg_type_header.msg_type_name = MSG_TYPE_CHAR;
	theMessage.dataType.msg_type_header.msg_type_size = 8;
	theMessage.dataType.msg_type_header.msg_type_number = 1;
	theMessage.dataType.msg_type_header.msg_type_inline = FALSE;
	theMessage.dataType.msg_type_header.msg_type_longform = TRUE;
	theMessage.dataType.msg_type_header.msg_type_deallocate = FALSE;
	theMessage.dataType.msg_type_long_name = MSG_TYPE_CHAR;
	theMessage.dataType.msg_type_long_size = 8;
	theMessage.dataType.msg_type_long_number = 1;
	
	theMessage.handle = 0;
	
	if ([self sendMessage:openSheetMesaDo :(void *)ws :strlen((char *)ws)] != MAPISuccess) {
		NXBeep();
		[self free];
		return NULL;
		}
		
	if ([self receiveMessage] != MAPISuccess || theMessage.head.msg_id != MAPISuccess ||
		!theMessage.handle) {
		NXBeep();
		[self free];
		return NULL;
		}
		
	return self;
	}

- initToNewWorksheet
{
	kern_return_t r;
	
	[super init];
	port_allocate(task_self(), &myPort);
	r = netname_look_up(name_server_port,"","Mesa-MAPI-1",&mesaPort);
	if (r != NETNAME_SUCCESS) {
		if (NXPortFromName("Mesa", "")) {
			r = netname_look_up(name_server_port,"","Mesa-MAPI-1",&mesaPort);
			if (r != NETNAME_SUCCESS) {
				NXBeep();
				[self free];
				return NULL;
				}
			}
		else {
			NXBeep();
			[self free];
			return NULL;
			}
		}

	theMessage.head.msg_simple = FALSE;
	theMessage.head.msg_size = sizeof(MAPIMessage);
	theMessage.head.msg_type = MSG_TYPE_NORMAL;
	theMessage.head.msg_local_port = myPort;
	theMessage.head.msg_remote_port = mesaPort;

	theMessage.lenType.msg_type_name = MSG_TYPE_INTEGER_32;
	theMessage.lenType.msg_type_size = 32;
	theMessage.lenType.msg_type_number = 1;
	theMessage.lenType.msg_type_inline = TRUE;
	theMessage.lenType.msg_type_longform = FALSE;
	theMessage.lenType.msg_type_deallocate = FALSE;

	theMessage.handleType = theMessage.lenType;
	
	theMessage.dataType.msg_type_header.msg_type_name = MSG_TYPE_CHAR;
	theMessage.dataType.msg_type_header.msg_type_size = 8;
	theMessage.dataType.msg_type_header.msg_type_number = 1;
	theMessage.dataType.msg_type_header.msg_type_inline = FALSE;
	theMessage.dataType.msg_type_header.msg_type_longform = TRUE;
	theMessage.dataType.msg_type_header.msg_type_deallocate = FALSE;
	theMessage.dataType.msg_type_long_name = MSG_TYPE_CHAR;
	theMessage.dataType.msg_type_long_size = 8;
	theMessage.dataType.msg_type_long_number = 1;
	
	theMessage.handle = 0;
	
	if ([self sendMessage:newSheetMesaDo :NULL :0] != MAPISuccess) {
		NXBeep();
		[self free];
		return NULL;
		}
		
	if ([self receiveMessage] != MAPISuccess || theMessage.head.msg_id != MAPISuccess ||
		!theMessage.handle) {
		NXBeep();
		[self free];
		return NULL;
		}
		
	return self;
	}

- (int)saveSheet
{
	int theError;
	
	theError = [self sendMessage:saveMesaDo :NULL :0];
	if (theError == MAPISuccess) theError = [self receiveMessage];
	return theError;
	}
	
- (int)saveSheetAs:(const char *)name
{
	int theError;
	
	theError = [self sendMessage:saveAsMesaDo :(void *)name :strlen(name) + 1];
	if (theError == MAPISuccess) theError = [self receiveMessage];
	
	return theError;
	}
	
- (int)hide
{
	int theError;
	
	theError = [self sendMessage:hideMesaDo :NULL :0];
	if (theError == MAPISuccess) theError = [self receiveMessage];
	return theError;
	}
	
- (int)makeKeyAndOrderFront
{
	int theError;
	
	theError = [self sendMessage:orderFrontMesaDo :NULL :0];
	if (theError == MAPISuccess) theError = [self receiveMessage];
	return theError;
	}

- (int)sendMessage:(int)m :(void *)data :(int)len
{
	kern_return_t r;
	
	theMessage.head.msg_local_port = myPort;
	theMessage.head.msg_remote_port = mesaPort;

	theMessage.head.msg_id = m;
	theMessage.theLen = len;
	theMessage.dataType.msg_type_long_number = len;
	if (data) theMessage.theData = data;
	else theMessage.theData = "";
	
	r = msg_send((msg_header_t *) &theMessage,SEND_TIMEOUT, 30000);
	if (r != SEND_SUCCESS) return MAPISendError;
	
	return MAPISuccess;
	}

- (int)receiveMessage
{
	kern_return_t r;

	r = msg_receive((msg_header_t *) &theMessage,RCV_TIMEOUT, 30000);
	if (r != RCV_SUCCESS) return MAPIReceiveError;
	
	return MAPISuccess;
	}
		
- free
{
	if (theMessage.handle) {
		[self sendMessage:unlinkSheetMesaDo :NULL :0];
		[self receiveMessage];
		}
		
	port_deallocate(task_self(), myPort);
	return [super free];
	}
	
- (int)recalc
{
	if ([self sendMessage:recalcMesaDo :NULL :0] != MAPISuccess) return MAPISendError;
	if ([self receiveMessage] != MAPISuccess) return MAPIReceiveError;
	return theMessage.head.msg_id;
	}

- (int)displaySheet
{
	if ([self sendMessage:displayMesaDo :NULL :0] != MAPISuccess) return MAPISendError;
	if ([self receiveMessage] != MAPISuccess) return MAPIReceiveError;
	return theMessage.head.msg_id;
	}

void freeMAPIValue(int n,MAPIValue *v)
{
	int x;
	MAPIValue *v1 = v;
	
	for (x = 0; x < n; x++,v++) if (v -> type == stringMAPIValue) free(v -> values.string);
	if (v1) free(v1);
	}

static void addString(NXStream *ns,const char *s)
{
	int x;
	
	if (!s) x = -1;
	else x = strlen(s);
	NXWrite(ns,&x,sizeof(int));
	
	if (x > 0) NXWrite(ns,s,x);
	}

static void addValue(NXStream *ns, MAPIValue *v)
{
	NXWrite(ns,&(v -> type),sizeof(int));
	NXWrite(ns,&(v -> row),sizeof(short));
	NXWrite(ns,&(v -> col),sizeof(short));
	switch (v -> type) {
		case stringMAPIValue:
			addString(ns,v -> values.string);
			break;
			
		case numberMAPIValue:
			NXWrite(ns,&(v -> values.number),sizeof(double));
			break;
		
		case errorMAPIValue:
			NXWrite(ns,&(v -> values.error),sizeof(double));
			break;
		}
	}
			
static char *getString(NXStream *ns)
{
	int x;
	char *s;
	
	NXRead(ns,&x,sizeof(int));
	if (x == -1) return NULL;
	s = malloc(malloc_good_size(x + 5));
	if (x) NXRead(ns,s,x);
	s[x] = 0;
	return s;
	}

static void getValue(NXStream *ns,MAPIValue *mv)
{
	NXRead(ns,&(mv -> type),sizeof(int));
	NXRead(ns,&(mv -> row),sizeof(short));
	NXRead(ns,&(mv -> col),sizeof(short));
	
	switch (mv -> type) {
		case stringMAPIValue:
			mv -> values.string = getString(ns);
			break;
			
		case numberMAPIValue:
			NXRead(ns,&(mv -> values.number),sizeof(double));
			break;
		
		case errorMAPIValue:
			NXRead(ns,&(mv -> values.error),sizeof(double));
			break;
		}
	}
		
- (int)associateLabel:(const char *)l withItems:(char **)it andValues:(MAPIValue *)mv
	offset:(int)off numItems:(int)ni orientation:(int)or;
{
	NXStream *ns;
	char *ad;
	int len,maxLen,theError = MAPISuccess;
	int x;
	
	ns = NXOpenMemory(NULL,0, NX_WRITEONLY);

	NXWrite(ns, &ni, sizeof(int));
	NXWrite(ns, &or, sizeof(int));
	NXWrite(ns, &off, sizeof(int));
	addString(ns, l);
	for (x = 0; x < ni; x++) {
		addString(ns, it[x]);
		addValue(ns, &mv[x]);
		}
	
	NXGetMemoryBuffer(ns, &ad, &len, &maxLen);
	theError = [self sendMessage:associateMesaDo :ad :len];
	NXCloseMemory(ns, NX_FREEBUFFER);
	if (theError == MAPISuccess) theError = [self receiveMessage];

	if (theError == MAPISuccess) theError = theMessage.head.msg_id;
	 
	return theError;
	}

- (int)getValues:(MAPIValue **)mv num:(int *)n
	upperRow:(int)ur col:(int)uc lowerRow:(int)lr col:(int)lc
{
	int theError;
	NXStream *ns;
	int x;
	short range[4];
	
	range[0] = ur;
	range[1] = uc;
	range[2] = lr;
	range[3] = lc;
	
	theError = [self sendMessage:getLabelRangeMesaDo :range :sizeof(short) * 4];
	if (theError == MAPISuccess && (theError = [self receiveMessage]) == MAPISuccess &&
		(theError = theMessage.head.msg_id) == MAPISuccess) {
		ns = NXOpenMemory(theMessage.theData,theMessage.theLen,NX_READONLY);
		
		NXRead(ns,n,sizeof(int));
		NXRead(ns,&x,sizeof(int));
		NXRead(ns,&x,sizeof(int));
		NXRead(ns,&x,sizeof(int));
		NXRead(ns,&x,sizeof(int));
		
		if (*n) {
			*mv = malloc(malloc_good_size(*n * sizeof(MAPIValue)));
			for (x = 0; x < *n; x++) getValue(ns,(*mv) + x);
			}
		else *mv = NULL;
		
		NXCloseMemory(ns, NX_FREEBUFFER);
		}
	
	if (theError == MAPISuccess && theMessage.theData) vm_deallocate(task_self(),(vm_address_t)
		theMessage.theData,theMessage.theLen);
	theMessage.theData = NULL;
	theMessage.theLen = 0;
	
	return theError;
	}

- (int)printReport:(char *)r
{	
	int theError;
	
	theError = [self sendMessage:printReportMesaDo :r :strlen(r) + 1];
	if (theError == MAPISuccess && (theError = [self receiveMessage]) == MAPISuccess)
		theError = theMessage.head.msg_id;
				
	return theError;
	}

- (int)getEPSForLabel:(char *)l in:(id *)vp
{	
	NXStream *ns;
	int theError;
	
	*vp = NULL;
	theError = [self sendMessage:getEPSForLabelMesaDo :l :strlen(l) + 1];
	if (theError == MAPISuccess && (theError = [self receiveMessage]) == MAPISuccess &&
		(theError = theMessage.head.msg_id) == MAPISuccess) {
		ns = NXOpenMemory(theMessage.theData,theMessage.theLen,NX_READONLY);
		*vp = [[NXImage alloc] initFromStream:ns];
		[*vp setDataRetained:YES];
		NXCloseMemory(ns, NX_FREEBUFFER);
		}
		
	return theError;
	}
	
- (int)getEPSForUpperRow:(int)ur col:(int)uc lowerRow:(int)lr col:(int)lc in:(id *)vp
{
	NXStream *ns;
	short range[4];
	int theError;
	
	range[0] = ur;
	range[1] = uc;
	range[2] = lr;
	range[3] = lc;

	*vp = NULL;
	theError = [self sendMessage:getEPSForRangeMesaDo :range :sizeof(short) * 4];
	if (theError == MAPISuccess && (theError = [self receiveMessage]) == MAPISuccess &&
		(theError = theMessage.head.msg_id) == MAPISuccess) {
		ns = NXOpenMemory(theMessage.theData,theMessage.theLen,NX_READONLY);
		*vp = [[NXImage alloc] initFromStream:ns];
		[*vp setDataRetained:YES];
		NXCloseMemory(ns, NX_FREEBUFFER);
		}
		
	return theError;
	}
	
- (int)getEPSForGraph:(char *)l in:(id *)vp
{	
	NXStream *ns;
	int theError;

	*vp = NULL;
	theError = [self sendMessage:getEPSForGraphMesaDo :l :strlen(l) + 1];
	if (theError == MAPISuccess && (theError = [self receiveMessage]) == MAPISuccess &&
		(theError = theMessage.head.msg_id) == MAPISuccess) {
		ns = NXOpenMemory(theMessage.theData,theMessage.theLen,NX_READONLY);
		*vp = [[NXImage alloc] initFromStream:ns];
		[*vp setDataRetained:YES];
		NXCloseMemory(ns, NX_FREEBUFFER);
		}
		
	return theError;
	}
	

- (int)getValues:(MAPIValue **)mv  forLabel:(const char *)l num:(int *)n
	upperRow:(int *)ur col:(int *)uc lowerRow:(int *)lr col:(int *)lc
{
	int theError;
	NXStream *ns;
	int x;
	
	theError = [self sendMessage:getLabelRangeMesaDo :(char *)l :strlen((char *)l) + 1];
	if (theError == MAPISuccess && (theError = [self receiveMessage]) == MAPISuccess &&
		(theError = theMessage.head.msg_id) == MAPISuccess) {
		ns = NXOpenMemory(theMessage.theData,theMessage.theLen,NX_READONLY);
		
		NXRead(ns,n,sizeof(int));
		NXRead(ns,ur,sizeof(int));
		NXRead(ns,uc,sizeof(int));
		NXRead(ns,lr,sizeof(int));
		NXRead(ns,lc,sizeof(int));
		
		if (*n) {
			*mv = malloc(malloc_good_size(*n * sizeof(MAPIValue)));
			for (x = 0; x < *n; x++) getValue(ns,(*mv) + x);
			}
		else *mv = NULL;
		
		NXCloseMemory(ns, NX_FREEBUFFER);
		}
	
	if (theError == MAPISuccess && theMessage.theData) vm_deallocate(task_self(),(vm_address_t)
		theMessage.theData,theMessage.theLen);
	theMessage.theData = NULL;
	theMessage.theLen = 0;
	
	return theError;
	}

- (int)getLabels:(char ***)lp ranges:(short **)r num:(int *)num
{
	int theError;
	NXStream *ns;
	int x;
	char **tlp;
	short *sp;
	
	theError = [self sendMessage:getLabelsMesaDo :NULL :0];
	if (theError == MAPISuccess && (theError = [self receiveMessage]) == MAPISuccess &&
		(theError = theMessage.head.msg_id) == MAPISuccess) {
		ns = NXOpenMemory(theMessage.theData,theMessage.theLen,NX_READONLY);
		
		NXRead(ns,num,sizeof(int));
		
		if (*num) {
			tlp = *lp = malloc(malloc_good_size(*num * sizeof(char *)));
			sp = *r = malloc(malloc_good_size(*num * 4 * sizeof(short)));
			
			for (x = 0; x < *num; x++) {
				tlp[x] = getString(ns);
				NXRead(ns,sp++,sizeof(short));
				NXRead(ns,sp++,sizeof(short));
				NXRead(ns,sp++,sizeof(short));
				NXRead(ns,sp++,sizeof(short));
				}
			}
		else {
			*lp = NULL;
			*r = NULL;
			}
		
		NXCloseMemory(ns, NX_FREEBUFFER);
		}
	
	if (theError == MAPISuccess && theMessage.theData) vm_deallocate(task_self(),(vm_address_t)
		theMessage.theData,theMessage.theLen);
	theMessage.theData = NULL;
	theMessage.theLen = 0;
	
	return theError;
	}

- (int)putValues:(MAPIValue *)mv num:(int)n
	upperRow:(int)ur col:(int)uc lowerRow:(int)lr col:(int)lc
{
	NXStream *ns;
	char *ad;
	int len,maxLen,theError = MAPISuccess;
	int x;
	short r,c;
	
	ns = NXOpenMemory(NULL,0, NX_WRITEONLY);

	NXWrite(ns, &n, sizeof(int));
	r = ur;
	c = uc;
	NXWrite(ns, &r, sizeof(short));
	NXWrite(ns, &c, sizeof(short));
	r = lr;
	c = lc;
	NXWrite(ns, &r, sizeof(short));
	NXWrite(ns, &c, sizeof(short));
	
	for (x = 0; x < n; x++) addValue(ns,mv + x);
	
	NXGetMemoryBuffer(ns, &ad, &len, &maxLen);
	theError = [self sendMessage:putRangeMesaDo :ad :len];
	NXCloseMemory(ns, NX_FREEBUFFER);
	if (theError == MAPISuccess) theError = [self receiveMessage];

	if (theError == MAPISuccess) theError = theMessage.head.msg_id;
	 
	return theError;
	}

@end

@implementation MesaListen

void MesaListenHandler(msg_header_t *vm,id mp)
{
	NXStream *ns;
	int num,ur,uc,lr,lc,x;
	MAPIValue *mv;
	
	MAPIMessage *msg = (MAPIMessage *) vm;

	ns = NXOpenMemory(msg -> theData,msg -> theLen,NX_READONLY);
	
	NXRead(ns,&num,sizeof(int));
	NXRead(ns,&ur,sizeof(int));
	NXRead(ns,&uc,sizeof(int));
	NXRead(ns,&lr,sizeof(int));
	NXRead(ns,&lc,sizeof(int));
	
	if (num) {
		mv = malloc(malloc_good_size(num * sizeof(MAPIValue)));
		for (x = 0; x < num; x++) getValue(ns,mv + x);
		}
	else mv = NULL;
	
	NXCloseMemory(ns, NX_FREEBUFFER);
	
	[mp gotMessage:mv num:num forUpperRow:ur upperCol:uc lowerRow:lr lowerCol:lc];
	
	freeMAPIValue(num,mv);
	
	if (msg -> theData) vm_deallocate(task_self(),(vm_address_t) msg -> theData, msg -> theLen);
	}


- initToPort:(char *)s
{
	kern_return_t r;

	[super init];
		
	portName = malloc(malloc_good_size(strlen(s) + 5));
	strcpy(portName,s);
	
	r = port_allocate(task_self(), &myPort);
	r = netname_check_in(name_server_port,portName,PORT_NULL, myPort);
	DPSAddPort(myPort,(DPSPortProc) MesaListenHandler,1024,self,30);

	return self;
	}
	
- free
{
	kern_return_t r;

	DPSRemovePort(myPort);
	r = netname_check_out(name_server_port,portName,PORT_NULL);
	r = port_deallocate(task_self(), myPort);

	free(portName);
	
	return [super free];
	}
	
- gotMessage:(MAPIValue *)mv num:(int)num forUpperRow:(int)ur upperCol:(int)uc lowerRow:(int)lr
	lowerCol:(int)lc
{
	return self;
	}

@end
